package typechecking;

import java.util.HashMap;

import symbol.ClassTable;
import symbol.ErrorMsg;
import symbol.MethodTable;
import syntaxtree.And;
import syntaxtree.ArrayAssign;
import syntaxtree.ArrayLength;
import syntaxtree.ArrayLookup;
import syntaxtree.Assign;
import syntaxtree.Block;
import syntaxtree.BooleanType;
import syntaxtree.Call;
import syntaxtree.ClassDeclExtends;
import syntaxtree.ClassDeclSimple;
import syntaxtree.False;
import syntaxtree.Formal;
import syntaxtree.Identifier;
import syntaxtree.IdentifierExp;
import syntaxtree.IdentifierType;
import syntaxtree.If;
import syntaxtree.IntArrayType;
import syntaxtree.IntegerLiteral;
import syntaxtree.IntegerType;
import syntaxtree.LessThan;
import syntaxtree.MainClass;
import syntaxtree.MethodDecl;
import syntaxtree.Minus;
import syntaxtree.NewArray;
import syntaxtree.NewObject;
import syntaxtree.Not;
import syntaxtree.Plus;
import syntaxtree.Print;
import syntaxtree.Program;
import syntaxtree.This;
import syntaxtree.Times;
import syntaxtree.True;
import syntaxtree.VarDecl;
import syntaxtree.While;
import visitor.Visitor;

public class SymbolTableVisitor implements Visitor {

        // utilizado para guardar os escopos atuais
        ClassTable currClass;
        MethodTable currMethod;
        String currVarId;
        boolean formalType;  // Indica se a variavel a ser adicionada e parametro formal ou nao( => variavel local)
        boolean returnType;  // Indica se o tipo representa o tipo de retorno de um metodo
        
        // lista de classes
        
        HashMap<String, ClassTable> symbolList = new HashMap<String, ClassTable>();
        
        // ErrorMsg
        
        ErrorMsg error = new ErrorMsg();
        
        // putWithError
        
        private boolean putClass(String className, ClassTable c) {
                if(symbolList.put(className, c) == null) return true;
                error.complain("class"+className+"is already defined");
                return false;
        }
        
        private boolean putMethod(ClassTable c, String m, MethodTable mt) {
                if(c.addMethod(m, mt)) return true;
                error.complain(m+"is already defined in "+c.getClassName());
                return false;
        }
        
        
        public HashMap<String, ClassTable> getSymbolTable() {
                return symbolList;
        }
        @Override
        public void visit(Program n) {
                // TODO Auto-generated method stub
                // chama recursivamente para main e para as classes de decl list
                formalType=false;
                returnType=false;
                currVarId=null;
                
                n.m.accept(this);
                for(int i = 0; i < n.cl.size(); i++)
                        n.cl.elementAt(i).accept(this); 
        }

        @Override
        public void visit(MainClass n) {
                // TODO Auto-generated method stub
                String className = n.i1.s;
                currClass = new ClassTable();
                currClass.setClassName(className);
                currMethod = new MethodTable();
                currMethod.setReturnType("void");
                currMethod.addParameterType("string[]",n.i2.toString()); // todo tipo estao em minusculo, a menos de def de classe
                currMethod.setMethodName("main");
                putMethod(currClass, currMethod.getMethodName(), currMethod);
                currClass.superClass = null;
                putClass(className, currClass);
                currClass = null;
                currMethod = null;
        }

        @Override
        public void visit(ClassDeclSimple n) {
                // TODO Auto-generated method stub
                String className = n.i.s;
                currClass = new ClassTable();
                currClass.setClassName(className);              
                for(int i = 0; i < n.vl.size(); i++) n.vl.elementAt(i).accept(this);
                for(int i = 0; i < n.ml.size(); i++) n.ml.elementAt(i).accept(this);
                currClass.superClass = null;
                putClass(className, currClass);
                currClass = null;
        }

        @Override
        public void visit(ClassDeclExtends n) {
                // TODO Auto-generated method stub
                String className = n.i.s;
                currClass = new ClassTable();
                currClass.setClassName(className);              
                for(int i = 0; i < n.vl.size(); i++) n.vl.elementAt(i).accept(this);
                for(int i = 0; i < n.ml.size(); i++) n.ml.elementAt(i).accept(this);
                currClass.superClass = n.j.toString();
                putClass(className, currClass);
                currClass = null;
        }

        @Override
        public void visit(VarDecl n) {
                // TODO Auto-generated method stub
                currVarId=n.i.toString();
                n.t.accept(this);
                currVarId=null;
        }

        @Override
        public void visit(MethodDecl n) {               
                // TODO Auto-generated method stub
                int i=0;
                
                currMethod = new MethodTable();
                currMethod.setMethodName(n.i.toString());
                
                returnType=true;
                n.t.accept(this); // saber Tipo de Retorno do metodo
                returnType=false;
                
                for(i = 0; i < n.fl.size(); i++) n.fl.elementAt(i).accept(this);                
                for(i = 0; i < n.vl.size(); i++) n.vl.elementAt(i).accept(this);
                for(i = 0; i < n.sl.size(); i++) n.sl.elementAt(i).accept(this);
                n.e.accept(this);
                putMethod(currClass, currMethod.getMethodName(), currMethod);
                currMethod = null;
        }

        @Override
        public void visit(Formal n) {
                // TODO Auto-generated method stub
                currVarId=n.i.toString();
                
                formalType=true;
                n.t.accept(this);  // Saber Type do parametro
                formalType=false;
                
                currVarId=null;         
        }

        @Override
        public void visit(IntArrayType n) {
                // TODO Auto-generated method stub
                if(returnType) {
                        currMethod.setReturnType("intarray");
                    return;
                }
                
                if(formalType) {
                        if(!(currMethod.addParameterType("intarray",currVarId))) error.complain(currVarId+"is already defined in "+currClass.getClassName()+"."+currMethod.getMethodName());
                    return;
                }
                        
        if(currMethod == null) {
                  if(!(currClass.addField(currVarId,"intarray"))) error.complain(currVarId+"is already defined in "+currClass.getClassName());
        } else if(!(currMethod.addVar(currVarId,"intarray"))) error.complain(currVarId+"is already defined in "+currClass.getClassName()+"."+currMethod.getMethodName());
        }

        @Override
        public void visit(BooleanType n) {
                // TODO Auto-generated method stub
                if(returnType) {
                        currMethod.setReturnType("bool");
                    return;
                }
                
                if(formalType) {
                        if(!(currMethod.addParameterType("bool",currVarId))) error.complain(currVarId+"is already defined in "+currClass.getClassName()+"."+currMethod.getMethodName());
                    return;
                }
                
                if(currMethod == null) {
                   if(!(currClass.addField(currVarId,"bool"))) error.complain(currVarId+"is already defined in "+currClass.getClassName());
            } else if(!(currMethod.addVar(currVarId,"bool"))) error.complain(currVarId+"is already defined in "+currClass.getClassName()+"."+currMethod.getMethodName());
        }

        @Override
        public void visit(IntegerType n) {
                // TODO Auto-generated method stub
                if(returnType) {
                        currMethod.setReturnType("int");
                    return;
                }
                
                if(formalType) {
                        if(!(currMethod.addParameterType("int",currVarId))) error.complain(currVarId+"is already defined in "+currClass.getClassName()+"."+currMethod.getMethodName());
                    return;
                }
                
                if(currMethod == null) {
                          if(!(currClass.addField(currVarId,"int"))) error.complain(currVarId+"is already defined in "+currClass.getClassName());
                } else if(!(currMethod.addVar(currVarId,"int"))) error.complain(currVarId+"is already defined in "+currClass.getClassName()+"."+currMethod.getMethodName());
        }

        @Override
        public void visit(IdentifierType n) {
                // TODO Auto-generated method stub
                if(returnType) {
                        currMethod.setReturnType(n.s);
                    return;
                }
                
                if(formalType) {
                        if(!(currMethod.addParameterType(n.s,currVarId))) error.complain(currVarId+"is already defined in "+currClass.getClassName()+"."+currMethod.getMethodName());
                    return;
                }
                
                if(currMethod == null) {
                          if(!(currClass.addField(currVarId,n.s))) error.complain(currVarId+"is already defined in "+currClass.getClassName());
                } else if(!(currMethod.addVar(currVarId,n.s))) error.complain(currVarId+"is already defined in "+currClass.getClassName()+"."+currMethod.getMethodName());
        }

        @Override
        public void visit(Block n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(If n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(While n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(Print n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(Assign n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(ArrayAssign n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(And n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(LessThan n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(Plus n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(Minus n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(Times n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(ArrayLookup n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(ArrayLength n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(Call n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(IntegerLiteral n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(True n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(False n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(IdentifierExp n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(This n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(NewArray n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(NewObject n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(Not n) {
                // TODO Auto-generated method stub

        }

        @Override
        public void visit(Identifier n) {
                // TODO Auto-generated method stub

        }

}